package util

import (
	"archive/zip"
	"fmt"
	"image/color"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"fyne.io/fyne/v2/data/binding"
)

// http://www.361way.com/golang-win-process/6105.html
// OpenWithDefaultApp 使用操作系统默认程序打开指定的文件
func OpenWithDefaultApp(path string) {
	var err error
	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", path).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start()
	case "darwin":
		err = exec.Command("open", path).Start()
	default:
		err = fmt.Errorf("unsupported platform")
	}
	if err != nil {
		// log.Fatal(err)
	}
}

// https://www.jianshu.com/p/4593cfffb9e9
// srcFile could be a single file or a directory
func Zip(srcFile string, destZip string) error {
	zipfile, err := os.Create(destZip)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
		// header.Name = path
		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
		}

		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if !info.IsDir() {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()
			_, err = io.Copy(writer, file)
		}
		return err
	})

	return err
}
func Unzip(zipFile string, destDir string) error {
	zipReader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer zipReader.Close()

	for _, f := range zipReader.File {
		fpath := filepath.Join(destDir, f.Name)
		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, os.ModePerm)
		} else {
			if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
				return err
			}

			inFile, err := f.Open()
			if err != nil {
				return err
			}
			defer inFile.Close()

			outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer outFile.Close()

			_, err = io.Copy(outFile, inFile)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func IsDir(pth string) bool {
	fi, err := os.Stat(pth)
	if err != nil {
		return false
	}
	return fi.IsDir()
}
func GetAudios(pth string) (lst []string) {
	fi, err := os.ReadDir(pth)
	if err != nil {
		return
	}
	for _, file := range fi {
		path1 := pth + string(filepath.Separator) + file.Name()
		ext := filepath.Ext(path1)
		if IsInSlice(ext, []string{".mp3", ".ogg", ".wav", ".flac"}) {
			lst = append(lst, path1)
		}
	}
	return
}
func GetFileList(pth string) (lst []string) {
	fi, err := ioutil.ReadDir(pth)
	if err != nil {
		return
	}
	for _, file := range fi {
		path1 := pth + string(filepath.Separator) + file.Name()
		lst = append(lst, path1)
	}
	return
}
func IsInSlice(value string, sli []string) bool {
	for _, v := range sli {
		if value == v {
			return true
		}
	}
	return false
}
func hex2rgb(str string) color.Color {
	if len(str) != 6 {
		return color.Black
	}
	r, _ := strconv.ParseInt(str[:2], 16, 10)
	g, _ := strconv.ParseInt(str[2:4], 16, 18)
	b, _ := strconv.ParseInt(str[4:], 16, 10)
	return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
}
func Alphabics() []string {
	return []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", ""}
}
func Rows(max int) (s []string) {
	for i := 1; i < max; i++ {
		s = append(s, strconv.Itoa(i))
	}
	return
}
func Add2List(dataList binding.StringList, col, row string) string {
	if col == "" || row == "" {
		return "please select col and row"
	}
	s := col + "-" + row
	ss, _ := dataList.Get()
	if IsInSlice(s, ss) {
		return "already in dataList"
	}
	dataList.Append(s)
	return "add success"
}

func Deletes(sl []string, s string) (ss []string) {
	if !strings.Contains(strings.Join(sl, ","), s) {
		return sl
	}
	for i, v := range sl {
		if v == s {
			ss = append(sl[:i], sl[i+1:]...)
		}
	}
	return
}
func Delete(sl []string, i int) []string {
	ss := append(sl[:i], sl[i+1:]...)
	return ss
}

// Max returns the larger of the passed values.
func Max(x, y int) int {
	if x > y {
		return x
	}
	return y
}

// IsExist 判断文件是否存在
func IsExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		if os.IsNotExist(err) {
			return false
		}
		return false
	}
	return true
}

// func GetEmbedFiles(pth, ext string) (lst []string) {
// 	if !IsDir(pth) {
// 		return
// 	}
// 	fi, err := srcs.ReadDir(pth)
// 	if err != nil {
// 		return
// 	}
// 	for _, file := range fi {
// 		path1 := pth + string(filepath.Separator) + file.Name()
// 		if IsDir(path1) {
// 			// lst = append(lst, path1)
// 			// lst=append(lst, GetEmbedFiles(file.Name(),ext)...)
// 			continue
// 		}
// 		if strings.Contains(file.Name(), "~") {
// 			continue
// 		}

// 		if filepath.Ext(path1) == ext {
// 			lst = append(lst, path1)
// 		} else if ext == "" {
// 			lst = append(lst, path1)
// 		}
// 	}
// 	return
// }

// 字节的单位转换 保留两位小数
// https://www.jb51.net/article/211051.htm
func formatFileSize(fileSize int64) (size string) {
	if fileSize < 1024 {
		//return strconv.FormatInt(fileSize, 10) + "B"
		return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
	} else if fileSize < (1024 * 1024) {
		return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
	} else if fileSize < (1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
	} else if fileSize < (1024 * 1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
	} else if fileSize < (1024 * 1024 * 1024 * 1024 * 1024) {
		return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
	} else { //if fileSize < (1024 * 1024 * 1024 * 1024 * 1024 * 1024)
		return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
	}
}

type Storage struct {
	Name       string
	FileSystem string
	Total      uint64
	Free       uint64
}

type storageInfo struct {
	Name       string
	Size       uint64
	FreeSpace  uint64
	FileSystem string
}

/* func getStorageInfo() {
	var storageinfo []storageInfo
	var loaclStorages []Storage
	err := wmi.Query("Select * from Win32_LogicalDisk", &storageinfo)
	if err != nil {
		return
	}

	for _, storage := range storageinfo {
		info := Storage{
			Name:       storage.Name,
			FileSystem: storage.FileSystem,
			Total:      storage.Size,
			Free:       storage.FreeSpace,
		}
		loaclStorages = append(loaclStorages, info)
	}
	fmt.Print("localStorages:=", loaclStorages)
} */

// func makeToolbar(ui *fyles) *fyne.Container {
// 	l := widget.NewLabel("")
// 	ui.filePath = l
// 	return container.NewBorder(nil, nil, widget.NewToolbar(
// 		widget.NewToolbarAction(theme.MoveUpIcon(), func() {
// 			p, err := storage.Parent(ui.pwd)
// 			if err != nil {
// 				return
// 			}
// 			ui.setDirectory(p)
// 		}),
// 		widget.NewToolbarAction(theme.HomeIcon(), func() {
// 			home, err := os.UserHomeDir()
// 			if err != nil {
// 				return
// 			}
// 			ui.setDirectory(storage.NewFileURI(home))
// 		}),
// 	), nil,
// 		container.NewHScroll(l))
// }

func TblCol(tbl, col string) string {
	return tbl + "/" + col
}

// pow is a fast multiply operator meant to replace the built-in math.Pow function
// for better performance, where the speed is much important than correctness.
// https://github.com/esimov/pigo/core/utils.go
func pow(base float64, exp int) float64 {
	result := 1.0
	for exp > 0 {
		if exp%2 == 1 {
			result *= base
		}
		exp >>= 1
		base *= base
	}
	return result
}